import numpy as np
import pandas as pd
from enum import Enum
import math

# 假设这里有一些节点的坐标，可以添加到上百个
# zs_map = [
#     (0, 0), (0, 1),(0, 2), (0, 3),(0, 4), (0, 5),(0, 6), (0, 7),(0, 8),(0, 9), (0, 10),(0, 11), (0, 12), (0, 13),(0, 14), (0, 15),
#     (1, 0), (1, 1),(1, 2), (1, 3),(1, 4), (1, 5),(1, 6), (1, 7),(1, 8),(1, 9), (1, 10),(1, 11), (1, 12), (1, 13),(1, 14), (1, 15),
#     (2, 0), (2, 1),(2, 2), (2, 3),(2, 4), (2, 5),(2, 6), (2, 7),(2, 8),(2, 9), (2, 10),(2, 11), (2, 12), (2, 13),(2, 14), (2, 15),
#     (3, 4),(3, 5),(3, 6), (3, 7),(3, 8),(3, 9), (3, 10),(3, 11), (3, 12), (3, 13),(3, 14), (3, 15)
# ]
# print(zs_map)

# ys_map
# ys_map = []
# for i in range(8):
#     for j in range(46):
#         k = (i, j)
#         ys_map.append(k)
# print(ys_map)
# ys_map = [
#     (0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (0, 6), (0, 7), (0, 8), (0, 9), (0, 10), (0, 11), (0, 12), (0, 13), (0, 14), (0, 15), (0, 16), (0, 17), (0, 18), (0, 19), (0, 20), (0, 21), (0, 22), (0, 23), (0, 24), (0, 25), (0, 26), (0, 27), (0, 28), (0, 29), (0, 30), (0, 31), (0, 32), (0, 33), (0, 34), (0, 35), (0, 36), (0, 37), (0, 38), (0, 39), (0, 40), (0, 41), (0, 42), (0, 43), (0, 44), (0, 45),
#     (1, 0), (1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (1, 6), (1, 7), (1, 8), (1, 9), (1, 10), (1, 11), (1, 12), (1, 13), (1, 14), (1, 15), (1, 16), (1, 17), (1, 18), (1, 19), (1, 20), (1, 21), (1, 22), (1, 23), (1, 24), (1, 25), (1, 26), (1, 27), (1, 28), (1, 29), (1, 30), (1, 31), (1, 32), (1, 33), (1, 34), (1, 35), (1, 36), (1, 37), (1, 38), (1, 39), (1, 40), (1, 41), (1, 42), (1, 43), (1, 44), (1, 45),
#     (2, 0), (2, 1), (2, 2), (2, 3), (2, 4), (2, 5), (2, 6), (2, 7), (2, 8), (2, 9), (2, 10), (2, 11), (2, 12), (2, 13), (2, 14), (2, 15), (2, 16), (2, 17), (2, 18), (2, 19), (2, 20), (2, 21), (2, 22), (2, 23), (2, 24), (2, 25), (2, 26), (2, 27), (2, 28), (2, 29), (2, 30), (2, 31), (2, 32), (2, 33), (2, 34), (2, 35), (2, 36), (2, 37), (2, 38), (2, 39), (2, 40), (2, 41), (2, 42), (2, 43), (2, 44), (2, 45),
#     (3, 0), (3, 1), (3, 2), (3, 3), (3, 4), (3, 5), (3, 6), (3, 7), (3, 8), (3, 9), (3, 10), (3, 11), (3, 12), (3, 13), (3, 14), (3, 15), (3, 16), (3, 17), (3, 18), (3, 19), (3, 20), (3, 21), (3, 22), (3, 23), (3, 24), (3, 25), (3, 26), (3, 27), (3, 28), (3, 29), (3, 30), (3, 31), (3, 32), (3, 33), (3, 34), (3, 35), (3, 36), (3, 37), (3, 38), (3, 39), (3, 40), (3, 41), (3, 42), (3, 43), (3, 44), (3, 45), (3, 46),
#     (4, 0), (4, 1), (4, 2), (4, 3), (4, 4), (4, 5), (4, 6), (4, 7), (4, 8), (4, 9), (4, 10), (4, 11), (4, 12), (4, 13), (4, 14), (4, 15), (4, 16), (4, 17), (4, 18), (4, 19), (4, 20), (4, 21), (4, 22), (4, 23), (4, 24), (4, 25), (4, 26), (4, 27), (4, 28), (4, 29), (4, 30), (4, 31), (4, 32), (4, 33), (4, 34), (4, 35), (4, 36), (4, 37), (4, 38), (4, 39), (4, 40), (4, 41), (4, 42), (4, 43), (4, 44), (4, 45),
#     (5, 0), (5, 1), (5, 2), (5, 3), (5, 4), (5, 5), (5, 6), (5, 7), (5, 8), (5, 9), (5, 10), (5, 11), (5, 12), (5, 13), (5, 14), (5, 15), (5, 16), (5, 17), (5, 18), (5, 19), (5, 20), (5, 21), (5, 22), (5, 23), (5, 24), (5, 25), (5, 26), (5, 27), (5, 28), (5, 29), (5, 30), (5, 31), (5, 32), (5, 33), (5, 34), (5, 35), (5, 36), (5, 37), (5, 38), (5, 39), (5, 40), (5, 41), (5, 42), (5, 43), (5, 44), (5, 45),
#     (6, 0), (6, 1), (6, 2), (6, 3), (6, 4), (6, 5), (6, 6), (6, 7), (6, 8), (6, 9), (6, 10), (6, 11), (6, 12), (6, 13), (6, 14), (6, 15), (6, 16), (6, 17), (6, 18), (6, 19), (6, 20), (6, 21), (6, 22), (6, 23), (6, 24), (6, 25), (6, 26), (6, 27), (6, 28), (6, 29), (6, 30), (6, 31), (6, 32), (6, 33), (6, 34), (6, 35), (6, 36), (6, 37), (6, 38), (6, 39), (6, 40), (6, 41), (6, 42), (6, 43), (6, 44), (6, 45),
#     (7, 0), (7, 1), (7, 2), (7, 3), (7, 4), (7, 5), (7, 6), (7, 7), (7, 8), (7, 9), (7, 10), (7, 11), (7, 12), (7, 13), (7, 14), (7, 15), (7, 16), (7, 17), (7, 18), (7, 19), (7, 20), (7, 21), (7, 22), (7, 23), (7, 24), (7, 25), (7, 26), (7, 27), (7, 28), (7, 29), (7, 30), (7, 31), (7, 32), (7, 33), (7, 34), (7, 35), (7, 36), (7, 37), (7, 38), (7, 39), (7, 40), (7, 41), (7, 42), (7, 43), (7, 44), (7, 45), (7, 46)
# ]

# tj_map = []
# for i in range(5):
#     for j in range(28):
#         k = (i+1, j+1)
#         tj_map.append(k)
# print(tj_map)
# tj_map = [
#     (1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (1, 6), (1, 7), (1, 8), (1, 9), (1, 10), (1, 11), (1, 12), (1, 13), (1, 14), (1, 15), (1, 16), (1, 17), (1, 18), (1, 19), (1, 20), (1, 21), (1, 22), (1, 23), (1, 24), (1, 25), (1, 26), (1, 27), (1, 28),
#     (2, 1), (2, 2), (2, 3), (2, 4), (2, 5), (2, 6), (2, 7), (2, 8), (2, 9), (2, 10), (2, 11), (2, 12), (2, 13), (2, 14), (2, 15), (2, 16), (2, 17), (2, 18), (2, 19), (2, 20), (2, 21), (2, 22), (2, 23), (2, 24), (2, 25), (2, 26), (2, 27), (2, 28),
#     (3, 1), (3, 2), (3, 3), (3, 4), (3, 5), (3, 6), (3, 7), (3, 8), (3, 9), (3, 10), (3, 11), (3, 12), (3, 13), (3, 14), (3, 15), (3, 16), (3, 17), (3, 18), (3, 19), (3, 20), (3, 21), (3, 22), (3, 23), (3, 24), (3, 25), (3, 26), (3, 27), (3, 28),
#     (4, 1), (4, 2), (4, 3), (4, 4), (4, 5), (4, 6), (4, 7), (4, 8), (4, 9), (4, 10), (4, 11), (4, 12), (4, 13), (4, 14), (4, 15), (4, 16), (4, 17), (4, 18), (4, 19), (4, 20), (4, 21), (4, 22), (4, 23), (4, 24), (4, 25), (4, 26), (4, 27), (4, 28),
#     (5, 1), (5, 2), (5, 3), (5, 4), (5, 5), (5, 6), (5, 7), (5, 8), (5, 9), (5, 10), (5, 11), (5, 12), (5, 13), (5, 14), (5, 15), (5, 16), (5, 17), (5, 18), (5, 19), (5, 20), (5, 21), (5, 22), (5, 23), (5, 24), (5, 25), (5, 26), (5, 27), (5, 28)
#     ]

#######################################################

class WeightType(Enum):
    """权重类型枚举"""
    UNIT = 1               # 单位权重：所有边权重为1
    EUCLIDEAN = 2          # 欧几里得距离：直线距离
    MANHATTAN = 3          # 曼哈顿距离：网格步长距离
    CHEBYSHEV = 4          # 切比雪夫距离：八连通网格距离


class MapBuilder:
    """地图构建类"""
    def __init__(self):
        self.coordinates = self.choose_coordinates(code=1)
        self.weight_type = WeightType

    def generate_map(
            self, 
            x_len: int, 
            y_len: int, 
            start_point: tuple
    ) -> list:
        """创建地图节点列表。

        Args:
            x_len: X轴长度
            y_len: Y轴长度
            start_point: 起点坐标 (0, 0) 或 (1, 1) 或 (2, 0) 或 (2, 1)

        Returns:
            list: 地图节点列表
        """
        map_list = []
        
        if start_point == (0, 0):
            for i in range(x_len):
                for j in range(y_len):
                    k = (i, j) 
                    map_list.append(k)
        elif start_point == (1, 1):
            for i in range(x_len):
                for j in range(y_len):
                    k = (i+1, j+1) 
                    map_list.append(k)
        elif start_point == (2, 0):
            for i in range(x_len):
                for j in range(y_len):
                    k = f"{i},{j}"
                    map_list.append(k)
        elif start_point == (2, 1):
            for i in range(x_len):
                for j in range(y_len):
                    k = f"{i+1},{j+1}"
                    map_list.append(k)
        else:
            map_list=[False, f"输入起点{start_point}错误, 请选择生成起点(0, 0)或(1, 1)"]

        return map_list
    
    def choose_coordinates(
            self, 
            code: int
    ) -> list:
        """选择地图节点列表。

        Args:
            code: 地图编号

        Returns:
            list: 地图节点列表
        """
        map_list = []

        if code == 0:
            map_list = self.generate_map(x_len=5, y_len=28, start_point=(0, 0))
        
        elif code == 1:
            map_list = [
                (1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (1, 6), (1, 7), (1, 8), (1, 9), (1, 10), (1, 11), (1, 12), (1, 13), (1, 14), (1, 15), (1, 16), (1, 17), (1, 18), (1, 19), (1, 20), (1, 21), (1, 22), (1, 23), (1, 24), (1, 25), (1, 26), (1, 27), (1, 28),
                (2, 1), (2, 2), (2, 3), (2, 4), (2, 5), (2, 6), (2, 7), (2, 8), (2, 9), (2, 10), (2, 11), (2, 12), (2, 13), (2, 14), (2, 15), (2, 16), (2, 17), (2, 18), (2, 19), (2, 20), (2, 21), (2, 22), (2, 23), (2, 24), (2, 25), (2, 26), (2, 27), (2, 28),
                (3, 1), (3, 2), (3, 3), (3, 4), (3, 5), (3, 6), (3, 7), (3, 8), (3, 9), (3, 10), (3, 11), (3, 12), (3, 13), (3, 14), (3, 15), (3, 16), (3, 17), (3, 18), (3, 19), (3, 20), (3, 21), (3, 22), (3, 23), (3, 24), (3, 25), (3, 26), (3, 27), (3, 28),
                (4, 1), (4, 2), (4, 3), (4, 4), (4, 5), (4, 6), (4, 7), (4, 8), (4, 9), (4, 10), (4, 11), (4, 12), (4, 13), (4, 14), (4, 15), (4, 16), (4, 17), (4, 18), (4, 19), (4, 20), (4, 21), (4, 22), (4, 23), (4, 24), (4, 25), (4, 26), (4, 27), (4, 28),
                (5, 1), (5, 2), (5, 3), (5, 4), (5, 5), (5, 6), (5, 7), (5, 8), (5, 9), (5, 10), (5, 11), (5, 12), (5, 13), (5, 14), (5, 15), (5, 16), (5, 17), (5, 18), (5, 19), (5, 20), (5, 21), (5, 22), (5, 23), (5, 24), (5, 25), (5, 26), (5, 27), (5, 28)
                ]

        else:
            map_list=[False, f"输入编号{code}错误, 请选择编号0或1"]

        return map_list

    def calculate_cost(
            self, 
            coord1: tuple, 
            coord2: tuple,
    ) -> float:
        """计算开销的函数(切比雪夫距离)。

        计算两个坐标之间的开销（距离），此处使用切比雪夫距离。

        Args:
            coord1: 坐标1 (x1, y1)
            coord2: 坐标2 (x2, y2)

        Returns:
            float: 计算出的开销值
        """
        x_diff = abs(coord1[0] - coord2[0])
        y_diff = abs(coord1[1] - coord2[1])
        return max(x_diff, y_diff)
    
    def calculate_weight(
            self, 
            coord1: tuple, 
            coord2: tuple, 
            weight_type: WeightType
    ) -> float:
        """计算两个坐标点之间的权重（开销/距离）。
        
        Args:
            coord1: 节点1的字符串坐标，如 (1,2)
            coord2: 节点2的字符串坐标，如 (2,3)
            weight_type: 权重计算类型，见 WeightType 枚举类
            
        Returns:
            float: 计算出的权重值

        Raises:
            ValueError: 如果提供了不支持的权重类型
        """
        # 解析字符串坐标为数值
        # x1, y1 = map(int, coord1.split(','))
        # x2, y2 = map(int, coord2.split(','))
        # dx = abs(x2 - x1)
        # dy = abs(y2 - y1)

        dx = abs(coord1[0] - coord2[0])
        dy = abs(coord1[1] - coord2[1])
        
        if weight_type == WeightType.UNIT:
            return 1.0
        elif weight_type == WeightType.EUCLIDEAN:
            return math.sqrt(dx**2 + dy**2)
        elif weight_type == WeightType.MANHATTAN:
            return dx + dy
        elif weight_type == WeightType.CHEBYSHEV:
            return max(dx, dy)
        else:
            raise ValueError("不支持的权重类型")

    def build_map_matrix(
            self, 
            is_save: bool = False
    ) -> np.ndarray:
        """构建地图矩阵。

        Args:
            is_save: 是否保存地图矩阵

        Returns:
            numpy.ndarray: 地图矩阵
        """
        
        # 创建一个空的距离矩阵
        num_nodes = len(self.coordinates)
        print(f"所有节点数量为: {num_nodes}")
        distance_matrix = np.zeros((num_nodes, num_nodes))

        # 生成距离矩阵
        for i in range(num_nodes):
            for j in range(num_nodes):
                if i != j:
                    # distance_matrix[i][j] = self.calculate_cost(self.coordinates[i], self.coordinates[j])
                    distance_matrix[i][j] = self.calculate_weight(
                        coord1=self.coordinates[i], 
                        coord2=self.coordinates[j],
                        weight_type=self.weight_type.EUCLIDEAN
                        )
                else:
                    distance_matrix[i][j] = 0  # 通常自身到自身的距离是0

        # 打印矩阵查看结果
        # print(distance_matrix)

        # 保存距离矩阵
        if is_save:
            df = pd.DataFrame(distance_matrix)
            # df.to_csv('app/map_planner/data/map_matrix_zs.csv', index=False, header=False)
            # df.to_csv('app/map_planner/data/map_matrix_qy.csv', index=False, header=False)
            df.to_csv('app/map_planner/data/map_matrix_tj.csv', index=False, header=False)

        return distance_matrix


class MapBase:
    """地图基础类"""
    def __init__(self):
        self.map_builder = MapBuilder()
        self.coordinates = self.map_builder.coordinates
        self.distance_matrix = self.load_distance_matrix(file_path="app/map_planner/data/map_matrix_tj.csv")

    def load_distance_matrix(
            self, 
            file_path: str
    ) -> np.ndarray:
        """加载距离矩阵。

        Args:
            file_path: CSV 文件路径

        Returns:
            numpy.ndarray: 距离矩阵
        """
        # 使用 pandas 从 CSV 文件加载距离矩阵
        df = pd.read_csv(file_path, header=None)
        return df.values
    
    def split_location_by_loc(
            self, 
            location: str
    ) -> dict:
        """分解坐标。

        Args:
            location: 库位坐标 "wh,x,y" - "库区,层,列"

        Returns:
            dict: 库位信息，形式为 {"warehouse": warehouse, "xy": "x,y"}
        """
        # 获取库位信息
        warehouse, x, y = location.split(",")
        return {"warehouse": int(warehouse), "xy": f"{x},{y}"}

    def find_node(
            self, 
            location: tuple
    ) -> list:
        """查找特定坐标并返回节点编号。

        Args:
            location: 待查找的坐标 -> (x, y)
        
        Returns:
            list: [True, int: 节点对应编号] or [False, str: 错误信息]
        """
        try:
            return [True, int(self.coordinates.index(location))]
        except ValueError:
            return [False, f"未找到坐标{location}"]
        
    def find_node_by_locstr(
            self, 
            location: str
    ) -> list:
        """查找特定坐标并返回节点编号。

        Args:
            location: 待查找的坐标 -> "x,y"
        
        Returns:
            list: [True, int: 节点对应编号] or [False, str: 错误信息]
        """
        try:
            # 找到对应坐标的节点
            loc =location.split(",")
            set_loc = (int(loc[0]), int(loc[1]))
            return [True, self.coordinates.index(set_loc)]
        except ValueError:
            return [False, f"未找到坐标({location})"]

    def find_location(
            self, 
            node: int
    ) -> list:
        """找到节点对应索引值。

        Args:
            node: int 节点索引值

        Returns:
            list: [True, (x, y)-节点对应的坐标] or [False, str: 错误信息]
        """
        try:
            location = self.coordinates[node]
            return [True , location]
        except ValueError:
            return [False, f"未找到节点 {node} 的坐标"]
        
    def find_location_str(
            self, 
            node: int
    ) -> list:
        """找到节点对应索引值。

        Args:
            node: 节点索引值

        Returns:
            list: [True, "x,y" - 节点对应的坐标] or [False, str: 错误信息]
        """
        try:
            location = self.coordinates[node]
            locstr = f"{location[0]},{location[1]}"
            return [True , locstr]
        except ValueError:
            return [False, f"未找到节点 {node} 的坐标"]
    
    def locstrs_to_nodes(
            self, 
            locstrs: list
    ) -> list:
        """将坐标字符串转换为节点索引值。
        
        Args:
            locstrs: 坐标字符串列表，格式为 ["x,y", "x,y", ...]
        
        Returns:
            list: 节点索引值列表，格式为 [node_index1, node_index2, ...]
        """
        node_list = []
        for loc in locstrs:
            node = self.find_node_by_locstr(loc)
            node_list.append(node[1])
        
        return node_list
    
    def nodes_to_locstrs(
            self,
            nodes: list
    ) -> list:
        """将节点索引值列表转换为坐标字符串列表。
        
        Args:
            nodes: 节点索引值列表 [node_index1, node_index2, ...]
        
        Returns:
            list: 坐标字符串列表 ["x,y", "x,y", ...]
        """
        locstr_list = []
        for node in nodes:
            location = self.find_location_str(node)
            locstr_list.append(location[1])

        return locstr_list

    def locset_to_locstr(
            self,
            loc_tuples: list
    ) -> list:
        """将坐标元组列表转坐标字符串列表。

        Args:
            loc_tuples: 元组列表 [(x,y), (x,y), ...]

        Returns:
            list: 坐标字符串列表 ['x,y', 'x,y', ...]
        """
        location_list = []

        for location in loc_tuples:
            location = f"{location[0]},{location[1]}"
            location_list.append(location)

        return location_list
    
    def locstr_for_db(
            self, 
            warehouse_id: int,
            loc_tuples: list
    ) -> list:
        """生成数据库使用的坐标字符串列表。
        
        - 将地图元组列表转坐标字符串列表
        - 添加库区编号，形式为 '库区,层,列'

        Args:
            warehouse_id: 库区编号
            loc_tuples: 元组列表 [(x,y), (x,y), ...]

        Returns:
            list: 坐标字符串列表 ['1,x,y', '1,x,y', ...]
        """
        location_list = []

        for location in loc_tuples:
            location = f"{warehouse_id},{location[0]},{location[1]}"
            location_list.append(location)

        return location_list